#include "configparser.h"
#include <cstdio>
#include <iostream>
#include <exception>

#include <stack>

ConfigParser::ConfigParser(const char* filename) : ioerror(false) {
  data = new configdata;
  
  yaml_parser_initialize(&this->parser);
  
  this->input = fopen(filename, "rb");
  if (this->input == NULL) {
    ioerror = true;
    perror(filename);
    return;
  }
  
  yaml_parser_set_input_file(&this->parser, this->input);
}

ConfigParser::~ConfigParser() {
  if (this->input)
    fclose(this->input);
  yaml_parser_delete(&this->parser);
  data->effects.clear();
  data->cursors.clear();
  data->menus.clear();
}

configdata* ConfigParser::getConfig() {
  return this->data;
}

bool ConfigParser::parse() {
  yaml_event_t event;
  
  VirtualItem* newSeq;
  VirtualItem* newMap;
  VirtualItem* lastItem;
  RootMapping* rm; // create the data repr from this
  
  stack<VirtualItem*> stack;
  
  BaseItem* base = new BaseItem();
  VirtualItem* cur = base;
  
  bool error = false;
  while(true) {
    if (!yaml_parser_parse(&this->parser, &event)) {
      cout << "Parser error at line " << this->parser.problem_mark.line
              << ", column " << this->parser.problem_mark.column
              << ". Message: " << this->parser.problem;
      error = true;
      break;
    }
    
    try {
      // handle the event
      switch (event.type) {
        case YAML_MAPPING_START_EVENT:
          newMap = cur->getMapping();
          stack.push(cur);
          cur = newMap;
          break;
        case YAML_SEQUENCE_START_EVENT:
          newSeq = cur->getSequence();
          stack.push(cur);
          cur = newSeq;
          break;
        case YAML_MAPPING_END_EVENT:
        case YAML_SEQUENCE_END_EVENT:
          if (!cur->checkCompleteness()) {
            cerr << "Incomplete definition. " << "Line: " <<
                    event.end_mark.line+1 << ". Column: " <<
                    event.end_mark.column+1 << ".";
            yaml_event_delete(&event);
            error = true;
            goto out;
          }
          
          if (stack.empty()) {
            cerr << "Internal error. Could not parse the config file. Line: "
                    << event.end_mark.line+1 << ". Column: " <<
                    event.end_mark.column+1 << ".";
            yaml_event_delete(&event);
            error = true;
            goto out;
          }
          
          lastItem = stack.top();
          stack.pop();
          lastItem->setNextItem(cur);
          if (stack.empty())
            // we bail out here, because we want cur to be a RootMapping
            // so that we could create the objects.
            break;
          cur = lastItem;
          break;
        case YAML_SCALAR_EVENT:
          cur->setNextItem(event.data.scalar.value, event.data.scalar.length);
          break;
        default: // don't process the rest of the events
          break;
      }
    } catch (BadConfigException& e) {
      cerr << e.what() << ". Line: " << event.start_mark.line+1
              << ". Column: " << event.start_mark.column+1 << ".";
      yaml_event_delete(&event);
      error = true;
      break;
    }
    
    if (event.type == YAML_STREAM_END_EVENT) {
      if (!stack.empty()) {
        cerr << "Unexpected end of stream for the configuration file. Line: "
                << event.end_mark.line+1 << ". Column: " <<
                event.end_mark.column+1 << ".";
        error = true;
      }
      yaml_event_delete(&event);
      
      // create the objects
      rm = dynamic_cast<RootMapping*>(cur);
      if (rm == NULL) {
        cerr << "Internal error. Cannot parse config file. Line: " <<
                event.end_mark.line+1 << ". Column: " <<
                event.end_mark.column+1 << ".";
        error = true;
      } else {
        rm->createObj(this->data);
      }
      
      break;
    }
    
    yaml_event_delete(&event);
  }
  
out:
  
  return !error;
}
